package com.bangcommunity.bbframe.common.utils.lang;

import com.bangcommunity.bbframe.common.annotation.BeanOrder;
import com.bangcommunity.bbframe.common.annotation.QtSpi;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author tanghc
 */
public abstract class SpiUtils {

    private static ConcurrentHashMap<Class, Boolean> clazzInitMap = new ConcurrentHashMap<>();
    private static ConcurrentHashMap<Class, List> clazzMap = new ConcurrentHashMap<>();
    private static ConcurrentHashMap<String, Object> spiMap = new ConcurrentHashMap<>();

    public static <T> void init(Class<T> clazz) {
        Boolean isInit = clazzInitMap.get(clazz);
        if (isInit != null && isInit) {
            return;
        }
        ServiceLoader<T> load = ServiceLoader.load(clazz);
        Set<T> set = new HashSet<>();
        if (null != load) {
            CollectionUtils.addAll(set, load.iterator());
            TreeMap<Integer, T> beans = new TreeMap<>();
            if (null != set) {
                for (T t : set) {
                    BeanOrder annotation = AnnotationUtils.findAnnotation(t.getClass(), BeanOrder.class);
                    int order = 1000;
                    if (null != annotation) {
                        order = annotation.order();
                    }
                    beans.put(order, t);
                    QtSpi qtSpi = AnnotationUtils.findAnnotation(t.getClass(), QtSpi.class);
                    String clazzName = "";
                    if (null != qtSpi) {
                        clazzName = qtSpi.value();
                    }
                    if (StringUtils.isBlank(clazzName)) {
                        clazzName = t.getClass().getSimpleName();
                    }
                    spiMap.putIfAbsent(clazzName, t);
                }
            }
            Collection<T> values = beans.values();
            if (null != values) {
                clazzMap.putIfAbsent(clazz, new ArrayList(values));
            }
        }
        clazzInitMap.putIfAbsent(clazz, Boolean.TRUE);
        return;
    }

    public static <T> List<T> listSpi(Class<T> clazz) {
        init(clazz);
        List list = clazzMap.get(clazz);
        return list;
    }

    public static <T> T getSpiBean(String name, Class<T> clazz) {
        init(clazz);
        T o = (T) spiMap.get(name);
        if (null == o) {
            List<T> ts = listSpi(clazz);
            if (CollectionUtils.isNotEmpty(ts)) {
                return ts.get(0);
            }
        }
        return o;
    }
}
